package com.gitee.huxing.course.order.core.sharding;

import cn.hutool.core.date.DateUtil;
import com.google.common.base.Preconditions;
import groovy.lang.Closure;
import groovy.util.Expando;
import lombok.extern.slf4j.Slf4j;
import org.apache.shardingsphere.sharding.algorithm.sharding.inline.InlineExpressionParser;
import org.apache.shardingsphere.sharding.api.sharding.standard.PreciseShardingValue;
import org.apache.shardingsphere.sharding.api.sharding.standard.RangeShardingValue;
import org.apache.shardingsphere.sharding.api.sharding.standard.StandardShardingAlgorithm;

import javax.annotation.PostConstruct;
import java.util.Collection;
import java.util.Properties;

/**
 * @Description:
 * @ProjectName: JavaCourse
 * @Package: com.gitee.huxing.course.order.core.sharding
 * @ClassName: PreciseShardingDBAlgorithm
 * @Author: huxing
 * @DateTime: 2021-09-23 下午6:39
 */
@Slf4j
public final class PreciseHintShardingAlgorithm implements StandardShardingAlgorithm<Comparable<?>> {

    private static final String ALGORITHM_EXPRESSION_KEY = "algorithm-expression";

    private static final String ALLOW_RANGE_QUERY_KEY = "allow-range-query-with-inline-sharding";

    private boolean allowRangeQuery;

    private Properties props = new Properties();

    @PostConstruct
    public void construct(){
        log.info("手动创建分片算法，加载正常{}", DateUtil.now());
    }

    private Closure<?> createClosure() {
        String expression = this.props.getProperty(ALGORITHM_EXPRESSION_KEY);
        Preconditions.checkNotNull(expression, "Inline sharding algorithm expression cannot be null.");
        String algorithmExpression = InlineExpressionParser.handlePlaceHolder(expression.trim());
        Closure<?> result = (new InlineExpressionParser(algorithmExpression)).evaluateClosure()
                .rehydrate(new Expando(), (Object)null, (Object)null);
        result.setResolveStrategy(3);
        return result;
    }

    @Override
    public String doSharding(Collection<String> availableTargetNames, PreciseShardingValue<Comparable<?>> shardingValue) {
        log.info("手动写一个强制路由处理: {}", DateUtil.now());
        Closure<?> closure = this.createClosure();
        closure.setProperty(shardingValue.getColumnName(), shardingValue.getValue());
        return closure.call().toString();
    }

    @Override
    public Collection<String> doSharding(Collection<String> availableTargetNames, RangeShardingValue<Comparable<?>> rangeShardingValue) {
        log.info("手动写一个强制路由处理: {}", DateUtil.now());
        if (this.allowRangeQuery) {
            return availableTargetNames;
        } else {
            throw new UnsupportedOperationException("Since the property of `allow-range-query-with-inline-sharding` is false, inline sharding algorithm can not tackle with range query.");
        }
    }

    @Override
    public void init() {
        this.allowRangeQuery = this.isAllowRangeQuery();
    }

    @Override
    public String getType() {
        return "INLINE-TEST";
    }

    @Override
    public Properties getProps() {
        return props;
    }

    @Override
    public void setProps(Properties props) {
        this.props = props;
    }

    private boolean isAllowRangeQuery() {
        return Boolean.parseBoolean(this.props.getOrDefault(ALLOW_RANGE_QUERY_KEY,
                Boolean.FALSE.toString()).toString());
    }
}
